// Fill out your copyright notice in the Description page of Project Settings.


#include "SimpleHttpFunctionLibrary.h"
#include "Containers/StringConv.h"
#include "Misc/SecureHash.h"
#include "Misc/Base64.h"
#include "Misc/FileHelper.h"
#include "sha256lib.h"
#include "GenericPlatform/GenericPlatformHttp.h"
#include <string>  
#include <sstream>  
#include <iomanip>
#include <assert.h> 
#include "JsonObjectWrapper.h"



FString USimpleHttpFunctionLibrary::GetFMD5(const FString& fstring)
{
	return FMD5::HashAnsiString(*fstring);
}

FString USimpleHttpFunctionLibrary::ToBase64(const TArray<uint8>& data)
{
	return FBase64::Encode(data);
}

bool USimpleHttpFunctionLibrary::Base64ToData(const FString& df, TArray<uint8>& data)
{
	if (FBase64::Decode(df, data, EBase64Mode::UrlSafe))
	{
		////GEngine->AddOnScreenDebugMessage(-1, 8.0f, FColor::Red, TEXT("读取失败"));
		return true;
	}
	return false;

}


static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

static inline bool is_base64(unsigned char c) {
	return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_decode(std::string const& encoded_string) {
	int in_len = encoded_string.size();
	int i = 0;
	int j = 0;
	int in_ = 0;
	unsigned char char_array_4[4], char_array_3[3];
	std::string ret;

	while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
		char_array_4[i++] = encoded_string[in_]; in_++;
		if (i == 4) {
			for (i = 0; i < 4; i++)
				char_array_4[i] = base64_chars.find(char_array_4[i]);

			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

			for (i = 0; (i < 3); i++)
				ret += char_array_3[i];
			i = 0;
		}
	}

	if (i) {
		for (j = i; j < 4; j++)
			char_array_4[j] = 0;

		for (j = 0; j < 4; j++)
			char_array_4[j] = base64_chars.find(char_array_4[j]);

		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

		for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
	}

	return ret;
}

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
	std::string ret;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];

	while (in_len--) {
		char_array_3[i++] = *(bytes_to_encode++);
		if (i == 3) {
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for (i = 0; (i < 4); i++)
				ret += base64_chars[char_array_4[i]];
			i = 0;
		}
	}

	if (i)
	{
		for (j = i; j < 3; j++)
			char_array_3[j] = '\0';

		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;

		for (j = 0; (j < i + 1); j++)
			ret += base64_chars[char_array_4[j]];

		while ((i++ < 3))
			ret += '=';

	}

	return ret;

}

bool USimpleHttpFunctionLibrary::Base64StrToStr(const FString& df, FString& outStr, bool UrlSafe)
{
	std::string str64 = TCHAR_TO_UTF8(*df);
	std::string out = base64_decode(str64);

	if (out.size()>=1)
	{
		outStr = UTF8_TO_TCHAR(out.c_str());
		return true;
	}
	return false;
	//outStr = "";
	//if (FBase64::Decode(df, outStr, UrlSafe?EBase64Mode::UrlSafe: EBase64Mode::Standard))
	//{
	//	return true;
	//}
	//return false;
 }



FString USimpleHttpFunctionLibrary::StringToBase64(const FString& data)
{

	const TCHAR* keychar = *data;
	int keyleng = FTCHARToUTF8_Convert::ConvertedLength(keychar, FCString::Strlen(keychar));
	TArray<uint8> key8;
	key8.Init(0, keyleng);
	memcpy(key8.GetData(), (uint8*)TCHAR_TO_UTF8(keychar), keyleng);
	FString out = FBase64::Encode(key8);
	key8.Empty();
	return out;
}

int base64_encode_buffer_size(const unsigned char* sourcedata, int datalength)
{
	int i = 0, j = 0;
	for (; i < datalength; i += 3) {
		j++;
		if (i + 1 < datalength) {
			j++;
		}
		else {
			j++;
			j++;
			j++;
			break;
		}
		if (i + 2 < datalength) {
			j++;
			j++;
		}
		else {
			j++;
			j++;
			break;
		}
	}
	j++;
	return j;
}

#include <string.h>


static const char* base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const char padding_char = '=';


FString  USimpleHttpFunctionLibrary::base64_encode_urlsafe(FString str)
{


	const TCHAR* keychar = *str;
	int keyleng = FTCHARToUTF8_Convert::ConvertedLength(keychar, FCString::Strlen(keychar));
	TArray<char> key8;
	key8.Init(0, keyleng);
	memcpy(key8.GetData(), (uint8*)TCHAR_TO_UTF8(keychar), keyleng);



	//char* sourcedata = TCHAR_TO_UTF8(*str);

	//int datalength = strlen(sourcedata);

	char* sourcedata = key8.GetData();
	int datalength = keyleng;//strlen(sourcedata);

	if (datalength <= 1)
	{
		return "";
	}

	char* base64 = new char[datalength * 8];

	int i = 0, j = 0;
	unsigned char trans_index = 0;
	for (; i < datalength; i += 3) {
		trans_index = ((sourcedata[i] >> 2) & 0x3f);
		base64[j++] = base64char[(int)trans_index];
		trans_index = ((sourcedata[i] << 4) & 0x30);
		if (i + 1 < datalength) {
			trans_index |= ((sourcedata[i + 1] >> 4) & 0x0f);
			base64[j++] = base64char[(int)trans_index];
		}
		else {
			base64[j++] = base64char[(int)trans_index];
			base64[j++] = padding_char;
			base64[j++] = padding_char;
			break;
		}
		trans_index = ((sourcedata[i + 1] << 2) & 0x3c);
		if (i + 2 < datalength) {
			trans_index |= ((sourcedata[i + 2] >> 6) & 0x03);
			base64[j++] = base64char[(int)trans_index];
			trans_index = sourcedata[i + 2] & 0x3f;
			base64[j++] = base64char[(int)trans_index];
		}
		else {
			base64[j++] = base64char[(int)trans_index];
			base64[j++] = padding_char;
			break;
		}
	}

	base64[j] = 0;

	FString outjson = base64;
	delete[] base64;
	return outjson;
}

bool USimpleHttpFunctionLibrary::SaveDataToDisk(const TArray<uint8>& data, FString str)
{
	return FFileHelper::SaveArrayToFile(data, *str);
}

bool USimpleHttpFunctionLibrary::SaveStringToDisk(const FString& data, FString str)
{
	return FFileHelper::SaveStringToFile(data, *str);
}



FString USimpleHttpFunctionLibrary::ReplaceStr(const FString souse, FString from, FString tostr)
{
	if (souse.Len() > from.Len() && from.Len() >= 1)
	{
		return souse.Replace(*from, *tostr);
	}
	return souse;
}

void USimpleHttpFunctionLibrary::ChangeAddTo2B(FString& od)
{
	//od.Replace(TEXT("+"), TEXT("%2B"));
}

FString USimpleHttpFunctionLibrary::ChangeAddTo2B_(FString od)
{
	//od.Replace(TEXT("+"), TEXT("%2B"));
	return od;
}


FString USimpleHttpFunctionLibrary::Utf8ToFString(const FString& str)
{
	return UTF8_TO_TCHAR(TCHAR_TO_ANSI(*str));
}


FString USimpleHttpFunctionLibrary::GetGignature(const FString& policy, const FString& access_key)
{
	const TCHAR* keychar = *access_key;
	int keyleng = FTCHARToUTF8_Convert::ConvertedLength(keychar, FCString::Strlen(keychar));
	uint8* key8 = new uint8[keyleng + 1];
	memcpy(key8, (uint8*)TCHAR_TO_UTF8(keychar), keyleng);
	key8[keyleng] = uint8();

	const TCHAR* policychar = *policy;
	int policyleng = FTCHARToUTF8_Convert::ConvertedLength(policychar, FCString::Strlen(policychar));
	uint8* policyuint8 = new uint8[policyleng + 1];
	memcpy(policyuint8, (uint8*)TCHAR_TO_UTF8(policychar), policyleng);
	policyuint8[policyleng] = uint8();

	TArray<uint8> dataBuffer;
	dataBuffer.Init(uint8(), 20);


	FSHA1::HMACBuffer((void*)key8, keyleng, (void*)policyuint8, policyleng, dataBuffer.GetData());

	for (int i = 0; i < 20; i++)
	{
		GLog->Log(FString::FromInt(dataBuffer[i]));
	}

	FString out = FBase64::Encode(dataBuffer);
	dataBuffer.Empty();

	return out;
}

bool  USimpleHttpFunctionLibrary::SplitURLParameter(const FString& str, TMap<FString, FString>& out)
{
	out.Reset();
	if (str.Len() < 2)
	{
		return false;
	}
	TArray<FString> Parameters;
	FString kv, k, v;
	str.ParseIntoArrayWS(Parameters, TEXT("&"));
	for (size_t i = 0; i < Parameters.Num(); i++)
	{
		kv = Parameters[i];
		if (kv.IsEmpty()) {
			continue;
		}

		if (kv.Split(TEXT("="), &k, &v))
		{
			out.Add(k, v);
		}
		else
		{
			out.Add(kv, kv);
		}
	}
	return out.Num() >= 1;
}

bool USimpleHttpFunctionLibrary::SplitJsonParameter(const FString& str, TMap<FString, FString>& out) 
{

	out.Reset();
	if (str.Len() < 4)
	{
		return false;
	}
	FJsonObjectWrapper wrapper;
	//读取data中的数据
	if (!wrapper.JsonObjectFromString(str))
	{
		return false;
	}

	FString s1,s2;

	for (const auto& v :wrapper.JsonObject->Values)
	{


		v.Value->Type;
		switch (v.Value->Type)
		{
		case EJson::Boolean:
			out.Add(v.Key, v.Value->AsBool() ? "true" : "false");
			break;
		case EJson::String:
			out.Add(v.Key, v.Value->AsString());
			break;
		case EJson::Number:
			s1 = FString::SanitizeFloat(v.Value->AsNumber());
			s2 = FString::Printf(TEXT("%lld"), v.Value->AsNumber());
			out.Add(v.Key, s2.Len()>= s1.Len()? s2: s1);	
			break;
		default:
			break;
		}
	} 
	return true;
 }


bool USimpleHttpFunctionLibrary::SplitString(const FString& str, FString l, FString r, FString& out)
{
	//GLog->Log("MK_SimpleHttpObj::SplitString");
	if (str.Len() > 3)
	{
		FString ll, rr;
		if (l.Len() >= 1 && str.Split(l, nullptr, &rr))
		{

			if (r.Len() == 0)
			{
				out = rr;
				return out.Len() >= 1;
			}
			return rr.Split(r, &out, nullptr);
		}

		if (r.Len() >= 1 && str.Split(r, &ll, nullptr))
		{

			if (l.Len() == 0)
			{
				out = ll;
				return out.Len() >= 1;
			}
			return rr.Split(l, nullptr, &out);
		}

	}
	return  false;
}



bool  USimpleHttpFunctionLibrary::StringToUint8(
	const FString& str, TArray<uint8>& outdata)
{

	if (str.Len() >= 1) {

		const TCHAR* keychar = *str;
		int keyleng = FTCHARToUTF8_Convert::ConvertedLength(keychar, FCString::Strlen(keychar));
		outdata.Init(0, keyleng);
		FTCHARToUTF8_Convert::Convert((char*)outdata.GetData(), keyleng, keychar, str.Len());
		return true;
	}

	//memcpy(outdata.GetData(), (uint8*)TCHAR_TO_UTF8(keychar), keyleng);


	return false;
}

FString  USimpleHttpFunctionLibrary::Uint8ToString(const TArray<uint8>& outdata)
{
	if (outdata.Num() >= 1) {

		FString outStr;
		FFileHelper::BufferToString(outStr, outdata.GetData(), outdata.Num());
		return outStr;

	}
	return "";
}


bool  USimpleHttpFunctionLibrary::MakeBodyData(
	FString Boundary, const TArray<FMKHttpBodyData>& bodyList
	, TArray<uint8>& RequestContent)
{

	if (Boundary.Len() < 4 || bodyList.Num() < 1)
	{
		return false;
	}

	FString channel = "6";
	FString topic = "6";

	RequestContent.Empty();

	FString BeginBoundry = "\r\n--" + Boundary + "\r\n";

	//*********************************

	FString debugOut;
	FString bodyStr;
	for (const FMKHttpBodyData& bodyData : bodyList)
	{

		if (bodyData.ContentDisposition.Len() >= 1 &&
			bodyData.dataName.Len() >= 1 &&
			bodyData.data.Num() >= 1)
		{
			RequestContent.Append((uint8*)TCHAR_TO_ANSI(*BeginBoundry), BeginBoundry.Len());

			bodyStr = "Content-Disposition: " + bodyData.ContentDisposition + ";";//文件头
			bodyStr.Append("name=\"" + bodyData.dataName + "\";");
			if (!bodyData.ContentType.IsEmpty() && (!bodyData.fileName.IsEmpty()))
			{
				bodyStr.Append("filename=\"" + bodyData.fileName + "\"");
				bodyStr.Append("\r\nContent-Type: " + bodyData.ContentType);
			}
			bodyStr.Append("\r\n\r\n");
			RequestContent.Append((uint8*)TCHAR_TO_ANSI(*bodyStr), bodyStr.Len());
			RequestContent.Append(bodyData.data);

			debugOut.Append(bodyStr);
			bodyStr.Reset();
		}
	}

	FString EndBoundary = "\r\n--" + Boundary + "--\r\n";
	RequestContent.Append((uint8*)TCHAR_TO_ANSI(*EndBoundary), EndBoundary.Len());
	return true;
}


FString USimpleHttpFunctionLibrary::GetSha256(const FString& str)
{
	//FString ss = TCHAR_TO_UTF8(*str);
	return SHA256Lib::ToSha256(str);

}

FString USimpleHttpFunctionLibrary::ToUrlEncode(const FString& str)
{
	return  FGenericPlatformHttp::UrlEncode(str);
}

FString USimpleHttpFunctionLibrary::GetSha256(const void* data, unsigned int numBytes)
{
	return SHA256Lib::ToSha256(data, numBytes);
}

FString  USimpleHttpFunctionLibrary::GetNowTickStr(int64 add)
{
	FDateTime  nowTime = FDateTime::Now();
	nowTime.GetMillisecond();
	uint64 tick = (nowTime.GetTicks() - FDateTime(1970, 1, 1).GetTicks()) / ETimespan::TicksPerMillisecond;
	tick = tick + add;
	return  FString::Printf(TEXT("%I64d"), tick);
}


unsigned char ToHex(unsigned char x)
{
	return x > 9 ? x + 55 : x + 48;
}

unsigned char FromHex(unsigned char x)
{
	unsigned char y = 0;
	if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
	else if (x >= 'a' && x <= 'z') y = x - 'a' + 10; else if (x >= '0' && x <= '9') y = x - '0';
	else assert(0);
	return y;
}


FString USimpleHttpFunctionLibrary::UrlEncode(const FString& inStr) 
{
	std::string str = TCHAR_TO_UTF8(*inStr);
	std::string strTemp = "";
	size_t length = str.length();
	for (size_t i = 0; i < length; i++)
	{
		if (isalnum((unsigned char)str[i]) ||
			(str[i] == '-') ||
			(str[i] == '_') ||
			(str[i] == '.') ||
			(str[i] == '~'))
			strTemp += str[i];
		else if (str[i] == ' ')
			strTemp += "+";
		else
		{
			strTemp += '%';
			strTemp += ToHex((unsigned char)str[i] >> 4);
			strTemp += ToHex((unsigned char)str[i] % 16);
		}
	}
	return strTemp.c_str();
}


FString USimpleHttpFunctionLibrary::UrlDecode(const FString& urlCode) {
	std::string encoded_str = TCHAR_TO_UTF8(*urlCode);


		std::string strTemp = "";
		size_t length = encoded_str.length();
		for (size_t i = 0; i < length; i++)
		{
			if (encoded_str[i] == '+')
				strTemp += ' ';
			else if (encoded_str[i] == '%')
			{
				assert(i + 2 < length);
				unsigned char high = FromHex((unsigned char)encoded_str[++i]);
				unsigned char low = FromHex((unsigned char)encoded_str[++i]);
				strTemp += high * 16 + low;
			}
			else strTemp += encoded_str[i];
		}
	
		return UTF8_TO_TCHAR(strTemp.c_str());
}